﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using MessageSystem.Default.infrastructure;
using MessageSystem.Default.Interface;

namespace MessageSystem.Default.implement
{
    unsafe public class MessageChannelBase
    {
        protected int Send(byte[] buf, int offset, int length)
        {
            int ret = 0;
            int transfered = 0;
            do
            {
                try
                {
                    ret = m_socket.Send(buf, offset, length, SocketFlags.None);
                    transfered += ret;
                    offset += ret;
                }
                catch (System.Exception)
                {
                    m_bWritable = false;
                    break;
                }
            }
            while (transfered != length);

            return transfered;
        }

        protected void Flush()
        {
            while (m_sndlist.count > 0 && m_bWritable)
            {
                msg pMsg = m_sndlist.head;

                int transfered = Send(pMsg.data.buffer, pMsg.offset(), (int)pMsg.getlen());
                if (0 != transfered)
                {
                    pMsg.advance((UInt32)transfered);
                    if (pMsg.getlen() == 0)
                    {
                        m_sndlist.get();
                        allocator.Instance.free_msg(pMsg);
                    }
                }
                else
                {
                    break;
                }
            }
        }

        protected void Recv()
        {
            int leftsize = m_nRecvBufferSize - m_nRecvSize;
            SocketError error;
            int transfered = m_socket.Receive(m_pRecvBuffer,m_nRecvSize,leftsize,SocketFlags.None,out error);
            if (transfered == 0 || error == SocketError.SocketError)
            {
                OnLost();
            }
            else
            {
                m_nRecvSize += transfered;
                int procLength = ProcessMsg(m_pRecvBufferPoint, m_nRecvSize);
                if (procLength != 0)
                {
                    m_nRecvSize = m_nRecvSize - procLength;
                    if (m_nRecvSize > 0)
                    {
                        Buffer.BlockCopy(m_pRecvBuffer, procLength, m_pRecvBuffer,0, m_nRecvSize);
                    }
                }
            }
        }

        private int ProcessMsg(byte* buffer, int nBufferLength)
        {
            int nTotalMsgSize = 0;
            Message.MessageHead* pMsgHead = (Message.MessageHead*)0;
            Message clientMsg = new Message();

            while ((pMsgHead = PeekMsg(buffer, nBufferLength)) != Message.MessageHead.Null)
            {
                clientMsg.Head = *pMsgHead;
                clientMsg.Body = (byte*)(pMsgHead + 1);
                clientMsg.BodyLength = pMsgHead->TotalLength - Message.MessageHead.Size;
                OnMsg(ref clientMsg);

                nTotalMsgSize += pMsgHead->TotalLength;
                buffer = &buffer[pMsgHead->TotalLength];
                nBufferLength -= pMsgHead->TotalLength;
            }

            return nTotalMsgSize;
        }

        private Message.MessageHead* PeekMsg(byte* buffer, int nBufferLength)
        {
            if (nBufferLength < Message.MessageHead.Size)
            {
                return (Message.MessageHead*)0;
            }
            else
            {
                Message.MessageHead* pMsgHead = (Message.MessageHead*)buffer;
                if (pMsgHead->TotalLength > options.max_message_size)
                {
                    OnInternalError(0);
                    return (Message.MessageHead*)0;
                }

                if (nBufferLength < pMsgHead->TotalLength)
                {
                    return (Message.MessageHead*)0;
                }

                return pMsgHead;
            }
        }

        protected virtual void OnLost()
        {

        }

        protected virtual void OnMsg(ref Message pMsg)
        {

        }

        protected virtual void OnInternalError(int error)
        {

        }

        protected Socket m_socket;
        protected list_msg m_sndlist;
        protected bool m_bWritable = true;
        protected byte[] m_pRecvBuffer;
        protected byte* m_pRecvBufferPoint;
        protected int m_nRecvBufferSize;
        protected int m_nRecvSize;
    }
}
